package com.epower.tools;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.HexUtil;
import com.epower.tools.bean.DatRowBean;
import com.epower.tools.bean.PntDataBean;

import java.math.BigInteger;
import java.util.*;

/**
 * @description: DAT文件读取
 * @author: LIU JUN
 * @create: 2021-03-21 22:58
 */
public class DatFileDataBase {

//    private final String datFileLocation;

    private byte[] content;


    /**
     * 采样记录
     */
    private List<DatRowBean> dataRows = null;

    /**
     * 以字节为单位读取文件，常用于读二进制文件，如图片、声音、影像等文件。
     */
//    public DatFileDataBase(String datFileLocation) {
//        this.datFileLocation = datFileLocation;
//    }
    public DatFileDataBase(byte[] content) {
        this.content = content;
    }

    final static int FOUR_BYTES = 4;

    public void parse(CfgFile cfgFile) throws Exception {
        Integer anaCounts = cfgFile.getAnalogChannelInfoList().size();
        Integer statusCounts = cfgFile.getStatusChannelInfoList().size();
        //每个采样记录的字节数
        int digitalBytesLen = 0;
        if (statusCounts % 16 != 0) {
            digitalBytesLen = 2 * ((statusCounts / 16) + 1);
        } else {
            digitalBytesLen = 2 * (statusCounts / 16);
        }
        Integer oneRowBytes = anaCounts * 2 + digitalBytesLen + FOUR_BYTES + FOUR_BYTES;
//        File file = new File(datFileLocation);
//        logger.debug(String.format("字节总数:%d,模拟通道数量:%d,状态通道数量:%d,每个采样要求的字节数:%d", datFileLocation.length(), anaCounts, statusCounts, oneRowBytes));

        // logger.debug("文件:" + file.getAbsolutePath() + ",Size:" + file.length() + "Bytes,LastUpdateTime:" + DateUtil.format(new Date(file.lastModified()), "yyyy-MM-dd HH:mm:ss.SSS"));
        //开始记录数据的时间微秒数

//        long startTimeMicroSeconds = cfgFile.toMicroSeconds(cfgFile.getFirstTimeText());

//        byte[] content = getContent(datFileLocation);
        int lines = content.length / oneRowBytes;
        this.dataRows = new ArrayList<>(lines);
        for (int i = 0; i < lines; i++) {
            //按每次采样开始读取
            DatRowBean datRowBean = new DatRowBean();
            //读取采样编号
            byte[] buffer = ArrayUtil.sub(content, 0 + i * oneRowBytes, 4 + i * oneRowBytes);
            datRowBean.setSampleIndex(bytesToInt(buffer));
            //读取时标
            buffer = ArrayUtil.sub(content, 4 + i * oneRowBytes, 8 + i * oneRowBytes);
            //使用时间倍率因子计算
//            long timeStamp = startTimeMicroSeconds + Double.valueOf(Long.valueOf(bytesToInt(buffer)) * cfgFile.getTimemultDouble()).longValue();
//            //时标已经使用时间倍率因子及dat存储的timestamp乘积加上开始时间计算过了
//            datRowBean.setTimeStamp(timeStamp);
            //读取模拟通道数据
            int idx = 0;
//            for (AnalogChannelInfo analogChannelInfo : cfgFile.getAnalogChannelInfoList()) {
//                buffer = ArrayUtil.sub(content, 8 + idx + i * oneRowBytes, 10 + idx + i * oneRowBytes);
//                //原始采样值*系数
//                datRowBean.addData(bytesToInt(buffer) * analogChannelInfo.chanMultiRate());
//                //转向下一个点
//                idx += 2;
//            }
            for (int j = 0; j < anaCounts; j++) {
                buffer = ArrayUtil.sub(content, 8 + idx + i * oneRowBytes, 10 + idx + i * oneRowBytes);
                //原始采样值*系数
                datRowBean.addData(bytesToInt(buffer));
                //转向下一个点
                idx += 2;
            }
            //读取状态通道数据
            buffer = ArrayUtil.sub(content, 10 + idx - 2 + i * oneRowBytes, oneRowBytes + i * oneRowBytes);
            if (ArrayUtil.isNotEmpty(buffer)) {
                List<Integer> byteBitStatusList = getByteArrayStatusList(buffer);
                datRowBean.getValues().addAll(byteBitStatusList.subList(0, statusCounts));
            }
            this.dataRows.add(datRowBean);
        }
    }


    /**
     * 字节数组转换为整数
     *
     * @param bytes
     * @return
     */
    public Integer bytesToInt(byte[] bytes) {
        String s = HexUtil.encodeHexStr(bytes).toUpperCase();
        String hexText = reverseHex(s);
        return hexText.length() == 4 ? parseHex4(hexText) : parseHex8(hexText);
    }

    /**
     * 将byte转换为一个长度为8的byte数组，数组每个值代表bit
     */

    public static List<Integer> getByteArrayStatusList(byte[] bArray) {
        List<Integer> byteBitStatusList = new ArrayList<>();
        for (byte b : bArray) {
            for (int i = 0; i < 8; i++) {
                if (((b >> i) & 0x01) == 1) {
                    byteBitStatusList.add(1);
                } else {
                    byteBitStatusList.add(0);
                }
            }
        }
        return byteBitStatusList;
    }

    /**
     * 转换高地位
     *
     * @param hex
     * @return
     */
    public static String reverseHex(String hex) {
        char[] charArray = hex.toCharArray();
        int length = charArray.length;
        int times = length / 2;
        for (int c1i = 0; c1i < times; c1i += 2) {
            int c2i = c1i + 1;
            char c1 = charArray[c1i];
            char c2 = charArray[c2i];
            int c3i = length - c1i - 2;
            int c4i = length - c1i - 1;
            charArray[c1i] = charArray[c3i];
            charArray[c2i] = charArray[c4i];
            charArray[c3i] = c1;
            charArray[c4i] = c2;
        }
        return new String(charArray);
    }

    public static Integer parseHex4(String num) {
        if (num.length() != 4) {
            throw new NumberFormatException("Wrong length: " + num.length() + ", must be 4.");
        }
        int ret = Integer.parseInt(num, 16);
        ret = ((ret & 0x8000) > 0) ? (ret - 0x10000) : (ret);
        return ret;
    }
    public static Integer parseHex8(String num) {
        if (num.length() != 8) {
            throw new NumberFormatException("Wrong length: " + num.length() + ", must be 8.");
        }
        BigInteger in = new BigInteger(num,16);
        return in.intValue();
    }


    /**
     * 查询指定点在特定的值
     *
     * @param rowNo
     * @return
     */
    public Object queryRowItemValue(Integer rowNo, Integer colNum) {
        if (rowNo > -1 && rowNo < this.dataRows.size() && colNum > -1) {
            DatRowBean rowValue = this.dataRows.get(rowNo);
            if (colNum < rowValue.getValues().size()) {
                return rowValue.getValues().get(colNum);
            }
        }
        return null;
    }

    /**
     * 查询指定点在特定的对象
     *
     * @param rowNo
     * @return
     */
    public PntDataBean queryRowItem(Integer rowNo, CfgFile cfgFile, String chId) {
        int colNum = cfgFile.findValPosition(chId);
        if (rowNo > -1 && rowNo < this.dataRows.size() && colNum > -1) {
            DatRowBean rowValue = this.dataRows.get(rowNo);
            if (colNum < rowValue.getValues().size()) {
                return new PntDataBean(chId, rowValue.getTimeStamp(), rowValue.getValues().get(colNum));
            }
        }
        return null;
    }

    public List<DatRowBean> getDataRows() {
        return dataRows;
    }

    /**
     * 返回全部的采样时间标签
     *
     * @return
     */
    public List<Long> getAllTimeStamp() {
        List<Long> allTimes = new ArrayList<>();
        for (DatRowBean datRowBean : dataRows) {
            allTimes.add(datRowBean.getTimeStamp());
        }
        return allTimes;
    }

    /**
     * 获取最大值
     *
     * @param cfgFile
     * @param chId
     * @return
     */
    public PntDataBean getMaxValue(CfgFile cfgFile, String chId) {
        return getMinMaxValue(cfgFile, chId, true);
    }

    /**
     * 获取最小值
     *
     * @param cfgFile
     * @param chId
     * @return
     */
    public PntDataBean getMinValue(CfgFile cfgFile, String chId) {
        return getMinMaxValue(cfgFile, chId, false);
    }

    private PntDataBean getMinMaxValue(CfgFile cfgFile, String chId, boolean isMax) {
        int colNum = cfgFile.findValPosition(chId);
        if (colNum < 0) {
            return null;
        }
        if (this.dataRows.isEmpty()) {
            return null;
        }
        Long timeStamp = this.dataRows.get(0).getTimeStamp();
        Integer firstValue = (Integer) this.dataRows.get(0).getValues().get(colNum);
        for (DatRowBean datRowBean : this.dataRows) {
            Integer tmp = (Integer) datRowBean.getValues().get(colNum);
            if (isMax) {
                if (tmp.compareTo(firstValue) > 0) {
                    firstValue = tmp;
                    timeStamp = datRowBean.getTimeStamp();
                }
            } else {
                if (tmp.compareTo(firstValue) < 0) {
                    firstValue = tmp;
                    timeStamp = datRowBean.getTimeStamp();
                }
            }

        }
        return new PntDataBean(chId, timeStamp, firstValue);
    }

}
